package com.example.logic.chapter23;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class SimpleJDKDynamicProxyDemo {

    interface IService {
        void sayHello();
    }


    static class RealService implements IService {
        @Override
        public void sayHello() {
            System.out.println("hello world");
        }
    }

    static class SimpleInvocationHandler implements InvocationHandler {
        private final Object object;

        public SimpleInvocationHandler(Object object) {
            this.object = object;
        }

        /**
         * proxy表示代理对象本身，需要注意，它不是被代理的对象，这个参数一般用处不大。
         * method表示正在被调用的方法。
         * args表示方法的参数。
         * 需要注意的是，不能将proxy作为参数传递给method. invoke
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("entering " + method.getName());
            Object ans = method.invoke(object, args);
            System.out.println("leaving " + method.getName());
            return ans;
        }
    }


    /**
     * Java SDK动态代理的局限在于，它只能为接口创建代理，返回的代理对象也只能转换到某个接口类型，
     * 如果一个类没有接口，或者希望代理非接口中定义的方法，那就没有办法了
     * <p>
     * 从代理的角度看，Java SDK代理的是对象，需要先有一个实际对象，自定义的InvocationHandler引用该对象，
     * 然后创建一个代理类和代理对象，客户端访问的是代理对象，代理对象最后再调用实际对象的方法；
     * cglib代理的是类，创建的对象只有一个。
     */
    public static void main(String[] args) {
        IService realService = new RealService();

        // newProxyInstance的返回值类型为Object，可以强制转换为interface数组中的某个接口类型
        // todo  理解newProxyInstance方法源码
        IService proxyService = (IService) Proxy.newProxyInstance(
                IService.class.getClassLoader() /*类加载器*/,
                new Class<?>[]{IService.class}  /*代理类要实现的接口与列表*/,
                new SimpleInvocationHandler(realService)    /*InvocationHandler*/
        );

        proxyService.sayHello();
    }


}
